package com.navinfo.platform.trip.common.arithmetic;

import com.navinfo.platform.trip.common.arithmetic.annotation.Arithmetic;
import com.navinfo.platform.trip.common.arithmetic.data.CommonData;
import com.navinfo.platform.trip.common.arithmetic.data.OuterEventData;
import com.navinfo.platform.trip.common.arithmetic.data.OuterStatisticData;
import com.navinfo.platform.trip.common.arithmetic.enums.EventStatus;
import com.navinfo.platform.trip.common.arithmetic.var.VarCommonNumber;
import com.navinfo.platform.trip.common.arithmetic.manager.EventConvert;
import com.navinfo.platform.trip.common.arithmetic.var.VarCarStart;
import com.navinfo.platform.trip.common.arithmetic.enums.EventType;
import com.navinfo.platform.trip.common.arithmetic.exception.CommonAssert;
import com.navinfo.platform.trip.common.arithmetic.manager.AbstractArithmetic;
import com.navinfo.platform.trip.common.config.ConfigMap;
import com.navinfo.platform.trip.common.util.ArithmeticUtils;

import java.math.BigDecimal;
import java.util.Optional;

/**
 * 车辆正常、冷车启步算法<br/>
 * 依赖{@link Gear}档位算法<br/>
 * 依赖{@link Mileage}里程油耗算法<br/>
 * 输出：统计、事件<br/>
 */
@Arithmetic(name = "正常启步,冷车启步算法")
public class CarStart extends AbstractArithmetic<VarCarStart> {
    /**
     * 事件类型
     */
    private EventType eventType = EventType.vehicleStart;
    /**
     * 正常启动
     */
    public static final int NORMAL_START = 1;
    /**
     * 冷车启动
     */
    public static final int COLD_START = 2;
    /**
     * 车辆起步时长阀值,单位毫秒
     */
    private long timeThreshold = ConfigMap.getLong("CAR_START_TIME_Threshold");
    /**
     * 车辆起步速度最大阀值单位:KM/H，转成m/s
     */
    private double speedThreshold = ArithmeticUtils.speedKmhToMs(ConfigMap.getInteger("CAR_START_SPEED_Threshold"));
    /**
     * 冷引擎启动温度阀值
     */
    private double coldThreshold = ConfigMap.getDouble("CAR_START_COLD_Threshold");

    @Override
    public void calculate() {
        logger.debug("进入车辆起步算法.");
        CommonData commonData = getCommonData();

        double preSpeed = Optional.ofNullable(commonData.getLastSpeedMs()).orElse(BigDecimal.valueOf(0.0)).doubleValue();
        double curSpeed = Optional.ofNullable(commonData.getSpeedMs()).orElse(BigDecimal.valueOf(0.0)).doubleValue();
        logger.debug("启动算法,GpsTime:[{}]===preSpeed:[{}]===curSpeed:[{}]", commonData.getGpsTime() / 1000, preSpeed, curSpeed);

        //当前点满足起步条件（前一个点速度为0，当前点速度小于阀值 ）
        if ((commonData.isTripFirstNode() || preSpeed == 0) && (curSpeed>0 && curSpeed< speedThreshold)) {
            logger.debug("起步算法，事件开始");
            OuterEventData eventData = startEventData(eventType);

            //事件开始时，将当前挡位放入事件的extendField01中。默认为0（Gear算法还没开始）
            eventData.setExtendField01(commonData.getGear());
            calcEventData(eventData);
        }
        // 已经在启动事件中了
        else if (drivingData.getEventData().containsKey(eventType)){
            OuterEventData eventData = drivingData.getEventData(eventType);
            CommonAssert.notNull(eventData, "事件中获取事件map为空异常！tid:" + terminalId);

            //大于速度阀值，事件结束
            if (curSpeed > speedThreshold) {
                logger.debug("启动算法，事件结束");
                calcEventData(eventData);
                EventConvert.convertEnd(eventData, drivingData);

                //extendField09是累计时长，大于事件持续时长则输出事件
                long duration = Optional.ofNullable(eventData.getExtendField09()).orElse(0L);
                if (duration > timeThreshold) {
                    logger.debug("启动算法，事件时间{},大于事件阀值{}，输出事件。", duration, timeThreshold);

                    //判断最后一个点水温，如果温度小于阀值 ，则为冷车启动
                    //extendField02放是否为冷车启动，NORMAL_START：正常启动，COLD_START：冷车启动
                    Integer waterTemp = commonData.getWaterTemp();
                    if (waterTemp != null && waterTemp <= coldThreshold) {
                        logger.debug("启动算法，事件结束时水温{},小于等于事件阀值{}，为冷车启动。", waterTemp, coldThreshold);
                        eventData.setExtendField02(COLD_START);
                    } else {
                        logger.debug("启动算法，事件结束时水温{}为空或大于事件阀值{}，为正常启动。", waterTemp, coldThreshold);
                        eventData.setExtendField02(NORMAL_START);
                    }

                    eventDataToVar(eventData);
                    drivingData.addEventDataToList(eventData);
                } else {
                    logger.debug("启动算法，事件时间{},小于事件阀值{}，不输出事件。", duration, timeThreshold);
                }
                drivingData.getEventData().remove(eventType);
            }else{
                logger.debug("启动算法，事件中");
                calcEventData(eventData);
            }
        } else {
            //非事件中，直接返回
            logger.debug("非启动事件中，不操作");
        }


        //如果行程结束，并且存在未完成的启动事件，直接丢弃
        if (commonData.isTripLastNode()) {
            Optional.ofNullable(drivingData.getEventData(eventType)).ifPresent(eventData ->{
                drivingData.removeEventData(eventType);
                logger.debug("行程结束，存在未完成的启动事件，直接丢弃,事件开始时间:{},累计时长:{}，", eventData.getStartGpsTime(), Optional.ofNullable(eventData.getExtendField09()).orElse(0L));
            });
        }

        logger.debug("车辆起步算法结束.");
    }


    /**
     * 累积事件的里程、油耗、时长
     */
    private void calcEventData(OuterEventData eventData) {
        //ExtendField05是累计里程，ExtendField06是累计油耗.
        EventConvert.convertCommonField(eventData, drivingData);
        //extendField09放入累计时长
        eventData.setExtendField09(getCommonData().getDffTime() + Optional.ofNullable(eventData.getExtendField09()).orElse(0L));

        if (eventData.getExtendField09() >= timeThreshold) {
            // 满足时长阈值后，需要推送事件开始，将状态设置成null
            eventData.setStatus(EventStatus.END.getValue());
        }

        logger.debug(">>事件数据,里程：{},油耗:{},时长:{},挡位:{},类型:{}",
                Optional.ofNullable(eventData.getExtendField05()).orElse(0.0),
                Optional.ofNullable(eventData.getExtendField06()).orElse(0.0),
                Optional.ofNullable(eventData.getExtendField09()).orElse(0L),
                Optional.ofNullable(eventData.getExtendField01()).orElse(0),
                Optional.ofNullable(eventData.getExtendField02()).orElse(0) == COLD_START ? "冷车启动" : "正常启动");
    }

    /**
     * 将事件中相关的指标，收集到统计变量中
     * @param eventData
     */
    private void eventDataToVar(OuterEventData eventData) {
        VarCarStart var = super.getObj();

        Double eventMileage = Optional.ofNullable(eventData.getExtendField05()).orElse(0.0);
        Double eventOil = Optional.ofNullable(eventData.getExtendField06()).orElse(0.0);
        Long eventTime = Optional.ofNullable(eventData.getExtendField09()).orElse(0L);

        //如果是冷车启动，则统计下冷车启动相关指标
        if (Optional.ofNullable(eventData.getExtendField02()).orElse(0) == COLD_START) {
            VarCommonNumber coldStart = var.getColdStart();
            coldStart.addNumber(1);
            coldStart.addMileage(eventMileage);
            coldStart.addFuel(eventOil);
            coldStart.addDuration(eventTime.intValue());
        }

        //不管是不是冷车启动，车启动相关指标都统计
        VarCommonNumber start = var.getStart();
        start.addNumber(1);
        start.addMileage(eventMileage);
        start.addFuel(eventOil);
        start.addDuration(eventTime.intValue());
    }

    @Override
    public void collect(OuterStatisticData statisticDataOut) {
        VarCarStart var = super.getObj();
        //冷车启动
        VarCommonNumber coldStart = var.getColdStart();
        statisticDataOut.setVehicleColdStartNumber(coldStart.getNumber());
        statisticDataOut.setVehicleColdStartFuel(ArithmeticUtils.fuelMlToMl(coldStart.getFuel()));
        statisticDataOut.setVehicleColdStartMileage(ArithmeticUtils.mileageMToM(coldStart.getMileage()));
        statisticDataOut.setVehicleColdStartDuration(ArithmeticUtils.timeMsToSecond(coldStart.getDuration()));

        //正常启动
        VarCommonNumber start = var.getStart();
        statisticDataOut.setVehicleStartNumber(start.getNumber());
        statisticDataOut.setVehicleStartFuel(ArithmeticUtils.fuelMlToMl(start.getFuel()));
        statisticDataOut.setVehicleStartMileage(ArithmeticUtils.mileageMToM(start.getMileage()));
        statisticDataOut.setVehicleStartDuration(ArithmeticUtils.timeMsToSecond(start.getDuration()));
    }


}
